#include "lista.h"
#include <stdio.h>
#include <stdlib.h>

typedef struct nodo{
	void* dato;
	struct nodo* siguiente;
}nodo_t;

struct lista {
	nodo_t* primero;
	nodo_t* ultimo;
	size_t largo;
};

struct iterador {
	nodo_t* actual;
	nodo_t* anterior;
};

                         /*Primitivas básicas*/
                        
nodo_t* crear_nodo (){
	nodo_t* aux =malloc(sizeof(nodo_t));
	if (aux == NULL) return NULL;
	return aux;
}							

lista_t *lista_crear(){
	lista_t* lista = malloc(sizeof(lista_t));
	if (lista == NULL) return NULL;
	lista->primero = NULL;
	lista->ultimo = NULL;
	lista->largo = 0;
	return lista;
}

bool lista_esta_vacia(const lista_t *lista){
	if (lista->largo == 0) return true;
	else return false;
}

bool lista_insertar_primero(lista_t *lista, void *dato){
	nodo_t* nuevo_nodo = crear_nodo();
	if (nuevo_nodo == NULL) return false;
	nuevo_nodo->dato = dato;
	nuevo_nodo->siguiente = lista->primero;
	if (lista_esta_vacia(lista)){
		lista->primero = nuevo_nodo;
		lista->ultimo = nuevo_nodo;
	}
	else{
		lista->primero = nuevo_nodo;
	}
	lista->largo ++;
	return true;
}

bool lista_insertar_ultimo(lista_t *lista, void *dato){
	nodo_t* nuevo_nodo = crear_nodo();
	if (nuevo_nodo == NULL) return false;
	nuevo_nodo->dato = dato;
	nuevo_nodo->siguiente = NULL;
	if (lista_esta_vacia(lista)){
		lista->ultimo = nuevo_nodo;
		lista->primero = nuevo_nodo;
	}
	else{
		lista->ultimo->siguiente = nuevo_nodo;
		lista->ultimo = nuevo_nodo;
	}
	lista->largo ++;
	return true;
}

void *lista_borrar_primero(lista_t *lista){
	nodo_t* aux;
	void* dato_primero;
	if (lista_esta_vacia(lista)) return NULL;
	aux = lista->primero;
	dato_primero = lista->primero->dato;
	if (lista->largo == 1){
		lista->primero = NULL;
		lista->ultimo = NULL;
	}
	else{
		lista->primero = lista->primero->siguiente;
	}
	free(aux);
	lista->largo --;
	return dato_primero;
}

void *lista_ver_primero(const lista_t *lista){
	if (lista_esta_vacia(lista)) return NULL;
    return lista->primero->dato;
}

size_t lista_largo(const lista_t *lista){
	return lista->largo;
}

void lista_destruir(lista_t *lista, void destruir_dato(void *)){
	void* dato;
	if (destruir_dato == NULL){
		while (! lista_esta_vacia(lista)){
			dato = lista_borrar_primero(lista);
		}
	}
	else{
		while (! lista_esta_vacia(lista)){
			dato = lista_borrar_primero(lista);
			destruir_dato(dato);
		}
	}
	free(lista);
	
}

                        /*Primitivas de iteración*/

lista_iter_t *lista_iter_crear(const lista_t *lista){
	lista_iter_t* iterador = malloc(sizeof(lista_iter_t));
	if (iterador == NULL) return NULL;
	iterador->actual = lista->primero;
	iterador->anterior = NULL;
	return iterador;
}

bool lista_iter_avanzar(lista_iter_t *iter){
	if (iter->actual == NULL) return false;
	iter->anterior = iter->actual;
	iter->actual = iter->actual->siguiente;
	return true;
}

void *lista_iter_ver_actual(const lista_iter_t *iter){
	if (iter->actual == NULL) return NULL;
	else return iter->actual->dato;
}

bool lista_iter_al_final(const lista_iter_t *iter){
	if (iter->actual == NULL) return true;
	else return false;
	
}

void lista_iter_destruir(lista_iter_t *iter){
	free(iter);
}
	
	

                        /*Primitivas de listas junto con iterador*/

bool lista_insertar(lista_t *lista, lista_iter_t *iter, void *dato){
	nodo_t* nodo_nuevo;
	nodo_nuevo = crear_nodo();
	if (nodo_nuevo == NULL) return false;
	nodo_nuevo->dato = dato;
	if(iter->anterior == NULL){
		nodo_nuevo->siguiente = iter->actual;
		iter->actual = nodo_nuevo;
        lista->primero = nodo_nuevo;
        if (lista_esta_vacia(lista)) lista->ultimo = nodo_nuevo;
	}
	else if(lista_iter_al_final(iter)){
			nodo_nuevo->siguiente = iter->actual;
			iter->anterior->siguiente = nodo_nuevo;
			iter->actual = nodo_nuevo;
			lista->ultimo = nodo_nuevo;
	}
	else{
		nodo_nuevo->siguiente = iter->actual;
		iter->anterior->siguiente = nodo_nuevo;
		iter->actual = nodo_nuevo;
	}
	lista->largo ++;
	return true;
}

void *lista_borrar(lista_t *lista, lista_iter_t *iter){
	nodo_t* nodo_borrado;
	void* dato_borrado;
	if(lista_iter_al_final(iter)) return NULL;
	if(lista_esta_vacia(lista)) return NULL;
	if (iter->anterior == NULL){
		dato_borrado = iter->actual->dato;
		nodo_borrado = iter->actual;
		iter->actual = iter->actual->siguiente;
		lista->primero = iter->actual;
		if(lista_largo(lista) == 1) lista->ultimo = iter->actual;
	}
	else if(iter->actual->siguiente == NULL){
			dato_borrado = iter->actual->dato;
			nodo_borrado = iter->actual;
			iter->actual = iter->actual->siguiente;
			iter->anterior->siguiente = iter->actual;
			lista->ultimo = iter->anterior;
	}
	else{
		dato_borrado = iter->actual->dato;
		nodo_borrado = iter->actual;
		iter->actual = iter->actual->siguiente;
		iter->anterior->siguiente = iter->actual;
	}
	free(nodo_borrado);
	lista->largo --;
	return dato_borrado;
}
	

